#pragma once

#include "RenderableObject.h"
#include <string>
#include <vector>
#include <d3dx9.h>
#include <d3d9.h>
#include "BoundingVolume.h"
#include "IScenery.h"


struct IDirect3DVertexBuffer9;
struct IDirect3DIndexBuffer9;
class CVertex;
class CGraphicsController;


///////////////////////////////////////////////////////////////////////////////

class CTerrainVertex
{
public:
   CTerrainVertex(float x, float y, float z, 
                  const D3DXVECTOR3& normal, 
                  float u0, float v0, 
                  float u1, float v1) 
      : m_coords(x, y, z), 
      m_normal(normal), 
      m_u0(u0), m_v0(v0), 
      m_u1(u1), m_v1(v1) {}

   D3DXVECTOR3 m_coords;
   D3DXVECTOR3 m_normal;
   float m_u0, m_v0;
   float m_u1, m_v1;

   static DWORD FVF;
};

///////////////////////////////////////////////////////////////////////////////

class CTerrainPatchMesh
{
public:
   private:
   CGraphicsController& m_graphicsController;
   IDirect3DVertexBuffer9* m_vertexBuffer;
   IDirect3DIndexBuffer9* m_indexBuffer;
   UINT m_facesCount;
   UINT m_verticesCount;
   D3DXVECTOR3 m_scale;

   UINT m_heightMapWidth;
   UINT m_heightMapHeight;
   UCHAR* m_heightMap;

   AABoundingBox m_boundingBox;

public:
   CTerrainPatchMesh(CGraphicsController& graphicsController, 
                     UCHAR* m_heightMap,
                     UINT heightMapWidth, UINT heightMapHeight,
                     UINT meshWidth, UINT meshHeight,
                     UINT offsetXOnMap, UINT offsetZOnMap,
                     D3DXVECTOR3 scale);
   ~CTerrainPatchMesh(void);

   void render(IDirect3DDevice9& d3Device) const;

   const AABoundingBox& getBoundingBox() const {return m_boundingBox;}

private:
   void createBuffers(UINT width, UINT height);

   void createVertices(UINT width, UINT height, UINT offsetXOnMap, UINT offsetZOnMap);

   void createIndices(UINT width, UINT height);

   D3DXVECTOR3 getNormal(UINT x, UINT z);
};

///////////////////////////////////////////////////////////////////////////////

class ITerrainRenderer
{
public:
   virtual ~ITerrainRenderer() {}

   virtual void render(CViewFrustrum& frustrum, 
                       IDirect3DDevice9& d3Device,
                       const std::vector<CTerrainPatchMesh*>& subMeshes,
                       IDirect3DTexture9& terrainTexture,
                       IDirect3DTexture9& detailTexture) const = 0;

};

///////////////////////////////////////////////////////////////////////////////

class CTerrainMesh : public IRenderableObject, public IScenery
{
private:
   CGraphicsController& m_graphicsController;
   ITerrainRenderer* m_renderer;
   std::vector<CTerrainPatchMesh*> m_subMeshes;
   D3DXVECTOR3 m_scale;

   UINT m_heightMapWidth;
   UINT m_heightMapHeight;
   UCHAR* m_heightMap;

   D3DMATERIAL9 m_terrainMat;
   IDirect3DTexture9* m_terrainTexture;
   IDirect3DTexture9* m_detailTexture;

   D3DXMATRIX m_mtxWorld;
   AABoundingBox m_boundingBox;

public:
   CTerrainMesh(CGraphicsController& graphicsController,
                const std::wstring& heightMapFileName,
                UINT heightMapWidth, UINT heightMapHeight,
                const std::wstring& terrainTextureFileName,
                const std::wstring& detailTextureFileName);
   ~CTerrainMesh(void);

   void render(CViewFrustrum& frustrum, IDirect3DDevice9& d3Device);

   D3DXVECTOR3 getIntersectionRemovalVector(const AABoundingBox& boundingBox) const;

   bool is3D() const {return true;}

private:
   void loadHeightMap(const std::wstring& heightMapFileName);

   void generateMesh(USHORT subMeshWidthInQuads, USHORT subMeshHeightInQuads);

   float getTerrainHeight(const D3DXVECTOR3& pos) const;
};

///////////////////////////////////////////////////////////////////////////////

class SinglePassTerrainRenderer : public ITerrainRenderer
{
public:
   void render(CViewFrustrum& frustrum, 
               IDirect3DDevice9& d3Device,
               const std::vector<CTerrainPatchMesh*>& subMeshes,
               IDirect3DTexture9& terrainTexture,
               IDirect3DTexture9& detailTexture) const;

};

///////////////////////////////////////////////////////////////////////////////

class MultiPassTerrainRenderer : public ITerrainRenderer
{
public:
   void render(CViewFrustrum& frustrum, 
               IDirect3DDevice9& d3Device,
               const std::vector<CTerrainPatchMesh*>& subMeshes,
               IDirect3DTexture9& terrainTexture,
               IDirect3DTexture9& detailTexture) const;

};

///////////////////////////////////////////////////////////////////////////////
